package main

import (
	"fmt"
	"time"
)

/*
需求：要求统计1~200000的数字中，哪些是素数？
分析思路：
传统的方法：就是使用一个循环，循环判断各个数是否是素数
使用并发/并行的方式，将统计素数的任务分配给多个(4)goroutine去完成，完成任务时间短
*/
//向intChan放入数
func putNum(intChan chan int) {
	for i := 1; i <= 5000; i++ {
		intChan <- i
	}
	//关闭intChan
	close(intChan)
}

//开启4个协程
func primeNum(intChan chan int, primeChan chan int, exitChan chan bool) {
	//使用for循环
	var flag bool
	for {
		// time.Sleep(time.Millisecond * 10)
		num, ok := <-intChan
		if !ok { //intChan取不到了
			break
		}
		flag = true //假设是素数
		//判断num是不是素数
		for i := 2; i < num; i++ {
			if num%i == 0 { //说明该num不是素数
				flag = false
				break
			}
		}
		if flag {
			//将这个数就放入到primeChan
			primeChan <- num
		}
	}
	fmt.Println("有一个primeNum协程因为取不到数据，退出")
	//这里我们还不能关闭primeChan
	//向退出exitChan写入true
	exitChan <- true
}
func main() {
	intChan := make(chan int, 1000)
	primeChan := make(chan int, 2000) //放入结果
	//标识退出的管道
	exitChan := make(chan bool, 4) //4个

	start := time.Now().Unix()
	//开始一个协程，想intChan放入1~8000个数
	go putNum(intChan)
	//开启4个协程，从intChan取出数据，并判断是否为素数，如果是，就放入到primeChan
	for i := 0; i < 4; i++ {
		go primeNum(intChan, primeChan, exitChan)
	}
	//这里主线程，进行处理
	go func() {
		for i := 0; i < 4; i++ {
			<-exitChan
		}
		end := time.Now().Unix()
		fmt.Println("使用协程耗时=", end-start)
		//当我们从exitChan取出了4个结果，就可以放心的关闭primeNum
		close(primeChan)
	}()
	//遍历我们的primeNum，把结果取出
	for {
		res, ok := <-primeChan
		if !ok {
			break
		}
		//将结果输出
		fmt.Printf("素数=%v\n", res)
	}
}
